using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Roslyn.Compilers;
using Roslyn.Compilers.CSharp;
namespace SymbolicComputation
{
    public class Transform
    {
        public SyntaxNode matchToMe;
        public SyntaxNode outPattern;
        public delegate Boolean MatchDelegate(SyntaxNode transformMe, SyntaxNode matchToMe);
        public event MatchDelegate Match;
        public class ReplacementPair
        {
            public String TransformOperand;
            public String Replacement;
        }

        public static String TransformToString(Transform transform)
        {
            return transform.matchToMe.ToString() + "~" + transform.outPattern.ToString();
        }

        public virtual String TransformString(String transformMe, String inTransform)
        {
            Transform transform = StringToTransform(inTransform);
            SyntaxNode equationTree = Miscellaneous.EquationToSyntaxNode(transformMe);
            SyntaxNode outTree = TransformSyntaxNode(equationTree, transform.matchToMe, transform.outPattern);
            if (outTree == null)
            {
                return "";
            }   
            return outTree.ToString();
        }

        public virtual SyntaxNode TransformSyntaxNode(SyntaxNode transformMe, Transform transform)
        {
            return TransformSyntaxNode(transformMe, transform.matchToMe, transform.outPattern);
        }

        public virtual SyntaxNode TransformSyntaxNode(SyntaxNode transformMe, SyntaxNode matchToMe, SyntaxNode outPattern)
        {
            Boolean didMatch = MatchWalk(transformMe,matchToMe);
            if (didMatch == true)
            {
                return TransformMatchedSyntaxNode(transformMe, matchToMe, outPattern);
            }
            return null;
        }

        public static Transform[] StringToTransforms(String[] inTransforms)
        {
            return Functional.Maps.Map(inTransforms, StringToTransform);
        }

        public static Transform StringToTransform(String inTransform)
        {
            String[] transformStrings = Miscellaneous.Split(inTransform, "~").ToArray();
            SyntaxNode[] transformTrees = Miscellaneous.EquationsToSyntaxNodes(transformStrings);
            Transform transform = new Transform();
            transform.matchToMe = transformTrees[0];
            transform.outPattern = transformTrees[1];
            return transform;
        }

        public static SyntaxNode TransformMatchedSyntaxNode(SyntaxNode transformMe,SyntaxNode matchToMe, SyntaxNode outPattern)
        {
            List<ReplacementPair> ReplacementPairs = new List<ReplacementPair>();
            Boolean foundMisMatch = false;
            GetReplacementPairs(matchToMe, transformMe, ref ReplacementPairs, ref foundMisMatch);
            if (foundMisMatch == false)
            {
                return ReplaceFunctions(outPattern, ReplacementPairs);
            }
            return null;
        }

        public static SyntaxNode ReplaceFunctions(SyntaxNode InTree, List<ReplacementPair> ReplacementPairs)
        {
            Int32 replacementPairIndex,descendantIndex;
            SyntaxNode outTree = Miscellaneous.CloneSyntaxNode(InTree);
            List<SyntaxNode> descendants = outTree.DescendentNodesAndSelf().ToList();
            SyntaxNode clonedNode;
            String descendentString;
            for (descendantIndex = descendants.Count - 1; descendantIndex >= 0; descendantIndex--)
            {
                for (replacementPairIndex = 0; replacementPairIndex < ReplacementPairs.Count; replacementPairIndex++)
                {

                    descendentString = descendants[descendantIndex].GetFullText();
                    if (descendentString == ReplacementPairs[replacementPairIndex].TransformOperand)
                    {
                        clonedNode = Miscellaneous.EquationToSyntaxNode(ReplacementPairs[replacementPairIndex].Replacement);
                        SyntaxNode parent = descendants[descendantIndex].Parent;
                        clonedNode = WrapNodeInBrackets(parent, clonedNode);
                        outTree = outTree.ReplaceNode(descendants[descendantIndex], clonedNode);
                        String test = outTree.ToString();
                        if (test.Contains("--") == true)
                        {
                            test = "";
                        }


                        descendants = outTree.DescendentNodesAndSelf().ToList();
                        break;
                    }
                }
            }
            return outTree;
        }

        public static SyntaxNode WrapNodeInBrackets(SyntaxNode parent, SyntaxNode wrapMe)
        {
            if (parent == null)
            {
                return wrapMe;
            }
            List<SyntaxNode> children;
            String wrapMeString = wrapMe.ToString();
            if (wrapMeString.Substring(0, 1) == "-")
            {
                if (parent.Kind != SyntaxKind.EqualsExpression)
                {
                    return Transform.WrapNodeInBrackets(wrapMe);
                }
            }
            children = parent.ChildNodes().ToList();
            if (parent.Kind == SyntaxKind.SubtractExpression | parent.Kind == SyntaxKind.DivideExpression)
            {
                if (wrapMe.Kind == SyntaxKind.AddExpression | wrapMe.Kind == SyntaxKind.SubtractExpression | wrapMe.Kind == SyntaxKind.MultiplyExpression | wrapMe.Kind == SyntaxKind.DivideExpression)
                {
                    return Transform.WrapNodeInBrackets(wrapMe);
                }
            }
            if (parent.Kind == SyntaxKind.NegateExpression)
            {
                if (wrapMe.Kind == SyntaxKind.AddExpression | wrapMe.Kind == SyntaxKind.SubtractExpression)
                {
                    return Transform.WrapNodeInBrackets(wrapMe);
                }
            }
            if (parent.Kind == SyntaxKind.MultiplyExpression)
            {
                if (wrapMe.Kind == SyntaxKind.AddExpression | wrapMe.Kind == SyntaxKind.SubtractExpression)
                {
                    return Transform.WrapNodeInBrackets(wrapMe);
                }
            }
            return wrapMe;
        }

        public static SyntaxNode WrapNodeInBrackets(SyntaxNode inNode)
        {
            String lStr = inNode.ToString();
            lStr = "(" + lStr + ")";
            return Miscellaneous.EquationToSyntaxNode(lStr);
        }

        //walk the transform tree and for each transform leaf get the matching equation in the Equation Tree
        public static void GetReplacementPairs(SyntaxNode matchToMe, SyntaxNode transformMe, ref List<ReplacementPair> ReplacementPairs, ref Boolean FoundMisMatch)
        {
            Int32 L;
            if (matchToMe.Kind == SyntaxKind.IdentifierName)
            {
                if (matchToMe.Parent.Kind != SyntaxKind.MemberAccessExpression)
                {
                    Boolean ReplacementPairExists = false;
                    //Search through the replacement pairs
                    for (L = 0; L < ReplacementPairs.Count; L++)
                    {
                        if (ReplacementPairs[L].TransformOperand == matchToMe.GetFullText())
                        {
                            ReplacementPairExists = true;
                            //The transform operand matches the replacement pair
                            if (ReplacementPairs[L].Replacement != transformMe.GetFullText())
                            {
                                FoundMisMatch = true;
                                break;
                            }
                        }
                    }
                    if (ReplacementPairExists == false)
                    {
                        ReplacementPair lPair = new ReplacementPair();
                        lPair.TransformOperand = matchToMe.GetFullText();
                        lPair.Replacement = transformMe.GetFullText();
                        ReplacementPairs.Add(lPair);
                    }
                }
            }
            if (FoundMisMatch == false)
            {
                if (matchToMe.HasChildren == true)
                {
                    List<SyntaxNode> matchToMeChildren = matchToMe.ChildNodes().ToList();
                    List<SyntaxNode> transformMeChildren = transformMe.ChildNodes().ToList();
                    if (transformMeChildren.Count >= matchToMeChildren.Count)
                    {
                        for (L = 0; L < matchToMeChildren.Count; L++)
                        {
                            GetReplacementPairs(matchToMeChildren[L], transformMeChildren[L], ref ReplacementPairs, ref FoundMisMatch);
                            if (FoundMisMatch == true)
                            {
                                return;
                            }
                        }
                    }
                    else
                    {
                        FoundMisMatch = true;
                        return;
                    }
                }
            }
        }

        public virtual Boolean MatchWalk(SyntaxNode transformMe, SyntaxNode matchToMe)
        {
            Boolean didMatch = Match(transformMe, matchToMe);
            if (didMatch == false)
            {
                return false;
            }
            if (matchToMe.HasChildren == true)
            {
                Int32 L;
                Boolean lBool;
                List<SyntaxNode> matchToMeChildren = matchToMe.ChildNodes().ToList();
                List<SyntaxNode> transformMeChildren = transformMe.ChildNodes().ToList();
                if (matchToMeChildren.Count > 0)
                {
                    if (matchToMeChildren.Count != transformMeChildren.Count)
                    {
                        return false;
                    }
                    for (L = 0; L < transformMeChildren.Count; L++)
                    {

                        lBool = MatchWalk(transformMeChildren[L], matchToMeChildren[L]);
                        if (lBool == false)
                        {
                            return false;
                        }
                    }
                }
            }
            return true;
        }
    }
}